﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows;

namespace Gmantis.Controls.DataGrid
{
    public enum DataGridRowType
    {
        Undefined,
        Item,
        New,
        Group,
        Top,
        Bottom
    }

    public enum DataGridGroupRowPosition
    {
        AboveData,
        BelowData
    }

    public class DataGridRow : DependencyObject, INotifyPropertyChanged
    {
        // Fields
        private bool _canUserResize = true;
        private bool _canUserToggleDetails = true;
        private object _dataItem;
        private Visibility _detailsVisibility = Visibility.Collapsed;
        private DataGridLength? _height = null;
        private bool _isEditing;
        private bool _isSelectable = true;
        private double? _maxHeight;
        private double? _minHeight;
        private DataGridRowCollection _parentCollection;
        private bool _showFreezingSeparator = true;
        private DataGridRowType _type;
        private Visibility _visibility;
        public static readonly DependencyProperty HeightProperty = DependencyProperty.Register("Height", typeof(DataGridLength), typeof(DataGridRow), new PropertyMetadata(new PropertyChangedCallback(DataGridRow.OnHeightPropertyChanged)));
        internal int LocalIndex = -1;
        public static readonly DependencyProperty MaxHeightProperty;
        public static readonly DependencyProperty MinHeightProperty;

        // Events
        public event EventHandler<PropertyChangedEventArgs<bool>> CanUserResizeChanged;

        public event EventHandler<PropertyChangedEventArgs<bool>> CanUserToggleDetailsChanged;

        public event EventHandler<PropertyChangedEventArgs<Visibility>> DetailsVisibilityChanged;

        internal event EventHandler<PropertyChangedEventArgs<Visibility>> DetailsVisibilityChanging;

        public event EventHandler<PropertyChangedEventArgs<DataGridLength>> HeightChanged;

        public event EventHandler<PropertyChangedEventArgs<bool>> IsEditingChanged;

        public event EventHandler Loaded;

        public event PropertyChangedEventHandler PropertyChanged;

        public event EventHandler<PropertyChangedEventArgs<Visibility>> VisibilityChanged;

        // Methods
        static DataGridRow()
        {
            MinHeightProperty = DependencyProperty.Register("MinHeight", typeof(double), typeof(DataGridRow), new PropertyMetadata(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                DataGridRow row = d as DataGridRow;
                row._minHeight = new double?((double)e.NewValue);
                row.UpdateActualMinHeight();
            }));
            MaxHeightProperty = DependencyProperty.Register("MaxHeight", typeof(double), typeof(DataGridRow), new PropertyMetadata(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                DataGridRow row = d as DataGridRow;
                row._maxHeight = new double?((double)e.NewValue);
                row.UpdateActualMaxHeight();
            }));
        }

        public DataGridRow()
        {
            this.IsEditable = true;
            this.Type = DataGridRowType.Undefined;
            this.Errors = new ObservableCollection<DataGridRowError>();
        }

        [Obsolete("Use OnBeginningRowEdit instead.", true)]
        protected internal virtual bool BeginRowEdit()
        {
            return true;
        }

        protected internal virtual void BindCellContent(FrameworkElement cellContent, DataGridColumn column)
        {
            column.BindCellContent(cellContent, this);
        }

        protected internal virtual void BindRowHeaderPresenter(DataGridRowHeaderPresenter presenter)
        {
        }

        protected internal virtual void BindRowPresenter(DataGridRowPresenter presenter)
        {
        }

        [Obsolete("Use OnCancelingRowEdit instead.", true)]
        protected internal virtual bool CancelRowEdit()
        {
            return true;
        }

        [Obsolete("Use OnCommitingRowEdit instead.", true)]
        protected internal virtual bool CommitRowEdit()
        {
            return true;
        }

        protected internal virtual FrameworkElement CreateCellContent(DataGridColumn column)
        {
            return column.CreateCellContent(this);
        }

        internal virtual DataGridCellPresenter CreateCellPresenter(DataGridColumn column)
        {
            return new DataGridCellPresenter();
        }

        protected internal virtual DataGridRowHeaderPresenter CreateRowHeaderPresenter()
        {
            return new DataGridRowHeaderPresenter();
        }

        protected internal virtual DataGridRowPresenter CreateRowPresenter()
        {
            return new DataGridRowPresenter();
        }

        protected internal virtual object GetCellContentRecyclingKey(DataGridColumn column)
        {
            return column.GetCellContentRecyclingKey(this);
        }

        protected internal virtual Style GetCellContentStyle(DataGridColumn column)
        {
            return column.ActualCellContentStyle;
        }

        internal FrameworkElement GetCellEditingContent(DataGridColumn column)
        {
            return column.GetCellEditingContent(this);
        }

        [Obsolete("Use Presenter[column] instead.")]
        public DataGridCellPresenter GetCellPresenter(DataGridColumn column)
        {
            return this.ViewRow.GetCellPresenter(column.ViewColumn, false);
        }

        internal virtual object GetCellPresenterRecyclingKey(DataGridColumn column)
        {
            return typeof(DataGridCellPresenter);
        }

        internal Style GetCellPresenterStyle(DataGridColumn column)
        {
            if (this.CellStyle != null)
            {
                return this.CellStyle;
            }
            return column.GetCellPresenterStyle();
        }

        protected internal virtual string GetCellText(DataGridColumn column)
        {
            return column.GetCellText(this);
        }

        protected internal virtual object GetCellValue(DataGridColumn column)
        {
            return column.GetCellValue(this);
        }

        protected internal virtual object GetDataItem()
        {
            if (this.Type == DataGridRowType.Item)
            {
                return this.ParentCollection.GetDataItem(this.LocalIndex);
            }
            return null;
        }

        internal DataGridLength GetHeight()
        {
            if (this._height.HasValue)
            {
                return this._height.Value;
            }
            return this.DefaultHeight;
        }

        protected internal virtual object GetRowHeaderPresenterRecyclingKey()
        {
            return typeof(DataGridRowHeaderPresenter);
        }

        internal virtual Style GetRowHeaderPresenterStyle()
        {
            if (this.RowHeaderStyle != null)
            {
                return this.RowHeaderStyle;
            }
            return this.DataGrid.RowHeaderStyle;
        }

        protected internal virtual object GetRowPresenterRecyclingKey()
        {
            return typeof(DataGridRowPresenter);
        }

        internal Style GetRowPresenterStyle()
        {
            if (this.RowStyle != null)
            {
                return this.RowStyle;
            }
            return this.DataGrid.RowStyle;
        }

        internal ViewRow GetViewRow(bool forceCreate)
        {
            if (this.ParentCollection != null)
            {
                return this.Model.DataGrid.View.GetViewRow(this, forceCreate);
            }
            return null;
        }

        protected internal virtual bool HasCellPresenter(DataGridColumn column)
        {
            return true;
        }

        internal bool IsVisible(bool includeCollapsedGroupRows = false)
        {
            if (this.Visibility == Visibility.Collapsed)
            {
                return false;
            }
            return (((this.ParentGroup == null) || includeCollapsedGroupRows) || ((this.ParentGroup.GroupRowsVisibility == Visibility.Visible) && this.ParentGroup.IsVisible(false)));
        }

        protected internal virtual bool OnBeganRowEdit()
        {
            return true;
        }

        protected internal virtual bool OnBeginningRowEdit()
        {
            DataGridEditingRowEventArgs e = new DataGridEditingRowEventArgs(this);
            this.DataGrid.OnBeginningRowEdit(e);
            if (!e.Cancel)
            {
                try
                {
                    this.Model.DataConnection.BeginEdit(this.DataItem);
                    return true;
                }
                catch
                {
                }
            }
            return false;
        }

        protected internal virtual bool OnCanceledRowEdit()
        {
            DataGrid dataGrid = this.DataGrid;
            this.Model.DataConnection.CancelEdit(this.DataItem);
            DataGridRowEventArgs e = new DataGridRowEventArgs(this);
            dataGrid.OnCanceledRowEdit(e);
            return true;
        }

        protected internal virtual bool OnCancelingRowEdit()
        {
            DataGridEditingRowEventArgs e = new DataGridEditingRowEventArgs(this);
            this.DataGrid.OnCancelingRowEdit(e);
            return !e.Cancel;
        }

        protected internal virtual bool OnCommitingRowEdit()
        {
            DataGridEditingRowEventArgs e = new DataGridEditingRowEventArgs(this);
            this.DataGrid.OnCommittingRowEdit(e);
            return !e.Cancel;
        }

        protected internal virtual bool OnCommittedRowEdit()
        {
            DataGrid dataGrid = this.DataGrid;
            try
            {
                this.Model.DataConnection.EndEdit(this.DataItem);
                dataGrid.OnCommittedRowEdit(new DataGridRowEventArgs(this));
                return true;
            }
            catch
            {
                return false;
            }
        }

        private void OnHeightChanged(DataGridLength newValue, DataGridLength oldValue)
        {
            if (this.HeightChanged != null)
            {
                PropertyChangedEventArgs<DataGridLength> e = new PropertyChangedEventArgs<DataGridLength>
                {
                    NewValue = newValue,
                    OldValue = oldValue
                };
                this.HeightChanged(this, e);
            }
        }

        private static void OnHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataGridRow row = d as DataGridRow;
            DataGridLength newValue = (DataGridLength)e.NewValue;
            if (!newValue.IsAbsolute)
            {
                DataGridLength length2 = (DataGridLength)e.NewValue;
                if (!length2.IsAuto)
                {
                    row.ClearValue(HeightProperty);
                    throw new ArgumentException("Star length is not allowed for DataGridRow Height");
                }
            }
            row.SetHeight();
        }

        protected internal virtual void OnLoaded()
        {
            this.DetailsVisibility = (this.DataGrid.RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.Visible) ? Visibility.Visible : Visibility.Collapsed;
        }

        private void OnLoaded(EventArgs e)
        {
            this.OnLoaded();
            if (this.Loaded != null)
            {
                this.Loaded(this, e);
            }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected internal virtual void OnUnloaded()
        {
        }

        private static void Refresh(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as DataGridRow).Refresh(true, true, true);
        }

        public void Refresh(bool refreshRow = true, bool refreshCells = true, bool refreshRowHeader = true)
        {
            if (this.ViewRow != null)
            {
                this.ViewRow.Unload(refreshRow, refreshCells, refreshRowHeader);
            }
        }

        protected internal virtual void SetCellValue(DataGridColumn column, object value)
        {
            column.SetCellValue(this, value);
        }

        private void SetHeight()
        {
            DataGridLength height = this.Height;
            if (!this._height.HasValue || (this._height.Value != height))
            {
                DataGridLength? nullable = this._height;
                DataGridLength oldValue = nullable.HasValue ? nullable.GetValueOrDefault() : DataGridLength.Auto;
                this._height = new DataGridLength?(height);
                this.OnHeightChanged(this._height.Value, oldValue);
            }
        }

        public override string ToString()
        {
            return string.Format("(Index = {0}, DataItem = {1})", this.Index, this.DataItem);
        }

        protected internal virtual void UnbindCellContent(FrameworkElement cellContent, DataGridColumn column)
        {
            column.UnbindCellContent(cellContent, this);
        }

        protected internal virtual void UnbindRowHeaderPresenter(DataGridRowHeaderPresenter presenter)
        {
        }

        protected internal virtual void UnbindRowPresenter(DataGridRowPresenter presenter)
        {
        }

        internal void UpdateActualMaxHeight()
        {
            this.Refresh(true, true, true);
        }

        internal void UpdateActualMinHeight()
        {
            this.Refresh(true, true, true);
        }

        internal void UpdateCanUserResize()
        {
            this.OnPropertyChanged("CanUserResize");
        }

        internal void UpdateMouseOver()
        {
            this.OnPropertyChanged("IsMouseOver");
        }

        internal void UpdateRowHeight(DataGridLength oldValue)
        {
            if (!this._height.HasValue)
            {
                this.OnHeightChanged(this.GetHeight(), oldValue);
            }
        }

        internal void UpdateSelection()
        {
            this.OnPropertyChanged("IsSelected");
        }

        public bool Validate()
        {
            return this.Validate(true);
        }

        public virtual bool Validate(bool validateAllProperties)
        {
            this.Errors.Clear();
            if (this.DataItem != null)
            {
                List<ValidationResult> validationResults = new List<ValidationResult>();
                ValidationContext validationContext = new ValidationContext(this.DataItem, null, null);
                if (Validator.TryValidateObject(this.DataItem, validationContext, validationResults, validateAllProperties))
                {
                    return true;
                }
                foreach (ValidationResult result in validationResults)
                {
                    this.Errors.Add(new DataGridRowError(result));
                }
            }
            return false;
        }

        // Properties
        public bool ActualCanUserToggleDetails
        {
            get
            {
                return (((this._canUserToggleDetails && this.DataGrid.CanUserToggleDetails) && this.DataGrid.IsDetailsTogglingAllowed) && (this.Type == DataGridRowType.Item));
            }
        }

        public Visibility ActualDetailsVisibility
        {
            get
            {
                if (this.Type != DataGridRowType.Item)
                {
                    return Visibility.Collapsed;
                }
                return this._detailsVisibility;
            }
        }

        public double ActualHeight { get; internal set; }

        internal double ActualMaxHeight
        {
            get
            {
                double? nullable = this._maxHeight;
                if (nullable.HasValue)
                {
                    return nullable.GetValueOrDefault();
                }
                if (this.DataGrid == null)
                {
                    return double.PositiveInfinity;
                }
                return this.DataGrid.MaxRowHeight;
            }
        }

        internal double ActualMinHeight
        {
            get
            {
                double? nullable = this._minHeight;
                if (nullable.HasValue)
                {
                    return nullable.GetValueOrDefault();
                }
                if (this.DataGrid == null)
                {
                    return 0.0;
                }
                return this.DataGrid.MinRowHeight;
            }
        }

        public bool CanUserResize
        {
            get
            {
                if (!this._canUserResize)
                {
                    return false;
                }
                if (this.DataGrid != null)
                {
                    return this.DataGrid.CanUserResizeRows;
                }
                return true;
            }
            set
            {
                bool flag = this._canUserResize;
                this._canUserResize = value;
                if ((this.CanUserResizeChanged != null) && (this._canUserResize != flag))
                {
                    PropertyChangedEventArgs<bool> e = new PropertyChangedEventArgs<bool>
                    {
                        NewValue = this._canUserResize,
                        OldValue = flag
                    };
                    this.CanUserResizeChanged(this, e);
                }
                this.OnPropertyChanged("CanUserResize");
            }
        }

        public bool CanUserToggleDetails
        {
            get
            {
                return this._canUserToggleDetails;
            }
            set
            {
                bool flag = this._canUserToggleDetails;
                this._canUserToggleDetails = value;
                if ((this.CanUserToggleDetailsChanged != null) && (this._canUserToggleDetails != flag))
                {
                    PropertyChangedEventArgs<bool> e = new PropertyChangedEventArgs<bool>
                    {
                        NewValue = this._canUserToggleDetails,
                        OldValue = flag
                    };
                    this.CanUserToggleDetailsChanged(this, e);
                }
                this.OnPropertyChanged("CanUserToggleDetails");
                this.OnPropertyChanged("ActualCanUserToggleDetails");
            }
        }

        public Style CellStyle { get; set; }

        public DataGrid DataGrid
        {
            get
            {
                if (this.ParentCollection != null)
                {
                    return this.Model.DataGrid;
                }
                return null;
            }
        }

        public object DataItem
        {
            get
            {
                if (!this.IsDataRowLoaded)
                {
                    object dataItem = this.GetDataItem();
                    if (dataItem != null)
                    {
                        this._dataItem = dataItem;
                    }
                }
                return this._dataItem;
            }
            internal set
            {
                this._dataItem = value;
            }
        }

        internal int DataSourceIndex
        {
            get
            {
                if (this.ParentCollection == null)
                {
                    return -1;
                }
                if (this.Type != DataGridRowType.Item)
                {
                    return -1;
                }
                return this.ParentCollection.DataSourceOffset(this);
            }
        }

        protected virtual DataGridLength DefaultHeight
        {
            get
            {
                if (this.DataGrid == null)
                {
                    return new DataGridLength(25.0);
                }
                return this.DataGrid.RowHeight;
            }
        }

        public DataGridDetailsPresenter DetailsPresenter
        {
            get
            {
                if ((this.DataGrid != null) && (this.DataGrid.View != null))
                {
                    return this.ViewRow.GetDetailsPresenter(false);
                }
                return null;
            }
        }

        public Visibility DetailsVisibility
        {
            get
            {
                return this._detailsVisibility;
            }
            set
            {
                Visibility visibility = this._detailsVisibility;
                if (visibility != value)
                {
                    if (this.DetailsVisibilityChanging != null)
                    {
                        PropertyChangedEventArgs<Visibility> e = new PropertyChangedEventArgs<Visibility>
                        {
                            NewValue = value,
                            OldValue = visibility
                        };
                        this.DetailsVisibilityChanging(this, e);
                    }
                    this._detailsVisibility = value;
                    if ((this.DetailsVisibilityChanged != null) && (this._detailsVisibility != visibility))
                    {
                        PropertyChangedEventArgs<Visibility> args2 = new PropertyChangedEventArgs<Visibility>
                        {
                            NewValue = this._detailsVisibility,
                            OldValue = visibility
                        };
                        this.DetailsVisibilityChanged(this, args2);
                    }
                    this.OnPropertyChanged("DetailsVisibility");
                    this.OnPropertyChanged("ActualDetailsVisibility");
                    if (this.DataGrid != null)
                    {
                        this.DataGrid.OnRowDetailsVisibilityChanged(new DataGridRowEventArgs(this));
                    }
                }
            }
        }

        public ObservableCollection<DataGridRowError> Errors { get; private set; }

        public DataGridRowHeaderPresenter HeaderPresenter
        {
            get
            {
                if ((this.DataGrid != null) && (this.DataGrid.View != null))
                {
                    return this.ViewRow.GetHeaderPresenter(false);
                }
                return null;
            }
        }

        public DataGridLength Height
        {
            get
            {
                return (DataGridLength)base.GetValue(HeightProperty);
            }
            set
            {
                base.SetValue(HeightProperty, value);
            }
        }

        public double IndentWidth
        {
            get
            {
                return (this.Level * this.DataGrid.IndentWidth);
            }
        }

        public int Index
        {
            get
            {
                if (this.ParentCollection == null)
                {
                    return -1;
                }
                return this.ParentCollection.IndexOf(this);
            }
        }

        internal bool IsBottomFrozen
        {
            get
            {
                int index = this.Index;
                return ((index >= 0) && (index >= (this.Model.Rows.Count - this.Model.FrozenBottomRowsCount)));
            }
        }

        internal bool IsDataRowLoaded
        {
            get
            {
                return (this._dataItem != null);
            }
        }

        public bool IsEditable { get; protected internal set; }

        public bool IsEditing
        {
            get
            {
                return this._isEditing;
            }
            internal set
            {
                bool flag = this._isEditing;
                this._isEditing = value;
                if ((this.IsEditingChanged != null) && (this._isEditing != flag))
                {
                    PropertyChangedEventArgs<bool> e = new PropertyChangedEventArgs<bool>
                    {
                        NewValue = this._isEditing,
                        OldValue = flag
                    };
                    this.IsEditingChanged(this, e);
                }
                this.OnPropertyChanged("IsEditing");
            }
        }

        internal bool IsFrozen
        {
            get
            {
                int index = this.Index;
                if (index < 0)
                {
                    return false;
                }
                if (index >= this.Model.FrozenTopRowsCount)
                {
                    return (index >= (this.Model.Rows.Count - this.Model.FrozenBottomRowsCount));
                }
                return true;
            }
        }

        public bool IsMouseOver
        {
            get
            {
                return this.DataGrid.View.IsMouseOverRow(this);
            }
        }

        public bool IsSelectable
        {
            get
            {
                return this._isSelectable;
            }
            set
            {
                this._isSelectable = value;
            }
        }

        public bool IsSelected
        {
            get
            {
                return this.DataGrid.Model.SelectionManager.SelectedRows.Contains(this);
            }
        }

        internal bool IsTopFrozen
        {
            get
            {
                int index = this.Index;
                return ((index >= 0) && (index < this.Model.FrozenTopRowsCount));
            }
        }

        public virtual int Level
        {
            get
            {
                return this.DataGrid.GroupedColumns.Length;
            }
        }

        public double MaxHeight
        {
            get
            {
                return (double)base.GetValue(MaxHeightProperty);
            }
            set
            {
                base.SetValue(MaxHeightProperty, value);
            }
        }

        public double MinHeight
        {
            get
            {
                return (double)base.GetValue(MinHeightProperty);
            }
            set
            {
                base.SetValue(MinHeightProperty, value);
            }
        }

        internal DataGridModel Model
        {
            get
            {
                if (this.ParentCollection != null)
                {
                    return this.ParentCollection.Model;
                }
                return null;
            }
        }

        public DataGridRowCollection ParentCollection
        {
            get
            {
                return this._parentCollection;
            }
            internal set
            {
                if (this._parentCollection != value)
                {
                    if (this._parentCollection != null)
                    {
                        this.OnUnloaded();
                    }
                    this._parentCollection = value;
                    if (value != null)
                    {
                        this.OnLoaded(new EventArgs());
                    }
                }
            }
        }

        public DataGridGroupRow ParentGroup
        {
            get
            {
                if (this.ParentCollection == null)
                {
                    return null;
                }
                return this.ParentCollection.OwnerGroup;
            }
        }

        public DataGridRowPresenter Presenter
        {
            get
            {
                if ((this.DataGrid != null) && (this.DataGrid.View != null))
                {
                    return this.ViewRow.GetRowPresenter(false);
                }
                return null;
            }
        }

        public Style RowHeaderStyle { get; set; }

        public Style RowStyle { get; set; }

        public bool ShowVerticalFreezingSeparator
        {
            get
            {
                return (this._showFreezingSeparator && (this.Model.VerticalFreezingSeparatorWidth > 0.0));
            }
            protected internal set
            {
                this._showFreezingSeparator = value;
            }
        }

        public DataGridRowType Type
        {
            get
            {
                return this._type;
            }
            internal set
            {
                this._type = value;
                this.OnPropertyChanged("DetailsVisibility");
            }
        }

        [Obsolete("Use Errors collection instead.", true)]
        public ObservableCollection<ValidationResult> ValidationResults { get; private set; }

        public double VerticalOffset
        {
            get
            {
                if (((this.DataGrid != null) && (this.DataGrid.View != null)) && this.DataGrid.View.IsLoaded)
                {
                    return this.Model.DataGrid.View.ScrollManager.GetScrollPositionFromRowIndex(this.Index);
                }
                return double.NaN;
            }
        }

        internal ViewRow ViewRow
        {
            get
            {
                return this.GetViewRow(true);
            }
        }

        public Visibility Visibility
        {
            get
            {
                return this._visibility;
            }
            set
            {
                Visibility visibility = this._visibility;
                if (visibility != value)
                {
                    this._visibility = value;
                    if (this.VisibilityChanged != null)
                    {
                        PropertyChangedEventArgs<Visibility> e = new PropertyChangedEventArgs<Visibility>
                        {
                            OldValue = visibility,
                            NewValue = value
                        };
                        this.VisibilityChanged(this, e);
                    }
                    this.OnPropertyChanged("Visibility");
                }
            }
        }
    }
}
